package com.file.parser;

import com.file.bo.ResponseData;
import com.file.bo.mail.PAB2;
import com.file.bo.mail.PABTran2;
import com.file.constant.ErrorCode;
import com.file.util.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSString;
import org.apache.pdfbox.pdfparser.PDFStreamParser;
import org.apache.pdfbox.pdfwriter.ContentStreamWriter;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDStream;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.text.PDFTextStripper;
import technology.tabula.*;
import technology.tabula.detectors.SpreadsheetDetectionAlgorithm;
import technology.tabula.extractors.SpreadsheetExtractionAlgorithm;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class PABPdfParser2 extends BasePdfParser {

    public ResponseData<String> parsePABPdfToJson(String daId, String filePath) {
        log.info("parsePABPdfToJson started, daId:{}", daId);
        String json = null;

        try {
            PAB2 pab = parsePABPdf(filePath);
            json = JsonUtils.convertObjectToJson(pab);
        } catch (Exception e) {
            log.error("OnError|{}||{}|{}|{}|{}", ErrorCode.FILE_PARSE_EXCEPTION.getCode(), daId, "", "", "parsePABPdfToJson failed", e);
            return new ResponseData<String>(json, ErrorCode.FILE_PARSE_EXCEPTION.getCode(), ErrorCode.FILE_PARSE_EXCEPTION.getMsg());
        }

        log.info("parsePABPdfToJson completed, daId:{}", daId);
        return new ResponseData<String>(json, ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg());
    }

    private PAB2 parsePABPdf(String filePath) {
        PAB2 pab = parsePABHeader(filePath);
        List<PABTran2> pabTrans = parsePabTrans(filePath);

        pab.setPabTrans(pabTrans);
        return pab;
    }

    private PAB2 parsePABHeader(String filePath) {
        PAB2 pab = new PAB2();
        String pdfHeaderText = parsePdfHeaderText(filePath);
        String inventoryNumber = pdfHeaderText.substring(pdfHeaderText.indexOf("清单编号：") + 5, pdfHeaderText.indexOf("开立日期")).trim();

        inventoryNumber = inventoryNumber.replaceAll("[^JYLSjyls0-9]", "");
        if (StringUtils.isBlank(inventoryNumber)) {
            throw new RuntimeException("parsePABHeader failed, inventoryNumber is null");
        }
        String issuanceDate = extractValue(pdfHeaderText, "开立日期：", "List Number：");
        if (pdfHeaderText.contains("清单编号") && pdfHeaderText.contains("户名：")) {
            String name = extractValue(pdfHeaderText, "户名：", "卡号/账号：");
            String accountNumber = extractValue(pdfHeaderText, "卡号/账号：", "存款类型：");
            String depositType = extractValue(pdfHeaderText, "存款类型：", "Account Name:");
            String currency = extractValue(pdfHeaderText, "币种：", "开户行：");
            String accountBank = extractValue(pdfHeaderText, "开户行：", "受理行：");
            String receivingBank = extractValue(pdfHeaderText, "受理行：", "Currency:");
            // 开户行存在换行
            if (currency.matches("[\\u4e00-\\u9fff]+[A-Za-z]+")) {
                String currency1 = currency;
                currency = currency1.replaceAll("[\\u4e00-\\u9fff]+", "");
                accountBank = currency1.replaceAll("[A-Za-z]+", "") + accountBank;
                String[] receivingBanks = receivingBank.split("\\s+");
                if (receivingBanks.length > 1) {
                    receivingBank = receivingBanks[receivingBanks.length - 1];
                    accountBank = accountBank + receivingBanks[0];
                }
            }

            String transactionDate = extractValue(pdfHeaderText, "交易起止日期：", "明细范围：");
            String detailRange = extractValue(pdfHeaderText, "明细范围：", "Transaction Starting-Ending Date:");

            pab.setInventoryNumber(inventoryNumber);
            pab.setIssuanceDate(issuanceDate);
            pab.setName(name);
            pab.setAccountNumber(accountNumber);
            pab.setDepositType(depositType);
            pab.setCurrency(currency);
            pab.setAccountBank(accountBank);
            pab.setReceivingBank(receivingBank);
            pab.setTransactionDate(transactionDate);
            pab.setDetailRange(detailRange);
        } else {
            pab.setInventoryNumber(inventoryNumber);
            pab.setIssuanceDate(issuanceDate);
        }

        return pab;
    }

    private String extractValue(String text, String startKey, String endKey) {
        if (text == null || !text.contains(startKey)) return "";

        int startIndex = text.indexOf(startKey) + startKey.length();
        int endIndex = (endKey != null && text.contains(endKey)) ? text.indexOf(endKey) : text.length();

        return text.substring(startIndex, endIndex).trim();
    }


    private List<PABTran2> parsePabTrans(String filePath) {
        List<PABTran2> pabTrans = new ArrayList<>();

        List<List<String>> tranFieldsList = parseFileToRowList(filePath);
        for (int i = 0; i < tranFieldsList.size(); i++) {
            List<String> row = tranFieldsList.get(i);
            if (!row.get(0).matches("\\d+")) {
                continue;
            }
            if (row.size() == 8) {
                PABTran2 pabTran = new PABTran2();
                pabTran.setId(row.get(0).trim());
                pabTran.setTransactionTime(row.get(1).trim());
                pabTran.setTransactionAmount(row.get(2).trim());
                pabTran.setBalance(row.get(3).trim());
                pabTran.setTradingPlace(row.get(4).trim());
                pabTran.setTransactionType(row.get(5).trim());
                pabTran.setComment(row.get(6).trim());
                pabTran.setCounterParty(row.get(7).trim());
                pabTrans.add(pabTran);
            } else if (row.size() == 7) {
                //  无交易对手信息
                PABTran2 pabTran = new PABTran2();
                pabTran.setId(row.get(0).trim());
                pabTran.setTransactionTime(row.get(1).trim());
                pabTran.setTransactionAmount(row.get(2).trim());
                pabTran.setBalance(row.get(3).trim());
                pabTran.setTradingPlace(row.get(4).trim());
                pabTran.setTransactionType(row.get(5).trim());
                pabTran.setComment(row.get(6).trim());
                pabTran.setCounterParty("");
                pabTrans.add(pabTran);
            }
        }
        return pabTrans;
    }

    private List<List<String>> parseFileToRowList(String filePath) {
        // 1. 读取文件
        File pdf = new File(filePath);

        //页面所有table的记录，每一个元素代表下面一条记录cellList
        List<List<String>> rowList = new ArrayList<>();

        // 2. pdfbox读取PDDocument
        try (PDDocument pdfDocument = PDDocument.load(pdf)) {
            for (PDPage pdPage : pdfDocument.getPages()) {
                removeWaterMark(pdPage, pdfDocument);
            }
            // 3. tabula新建ObjectExtractor和NurminenDetectionAlgorithm，同时准备接收表格Rectangle的结构
            ObjectExtractor objectExtractor = new ObjectExtractor(pdfDocument);
            SpreadsheetDetectionAlgorithm detectionAlgorithm = new SpreadsheetDetectionAlgorithm();
            Map<Integer, List<Rectangle>> pageNumber2RectangleListMap = new HashMap<>();//key为页的序号， value为对应的Rectangle的list

            // 4. 获取每页的PageIterator
            PageIterator pageIterator = objectExtractor.extract();

            // 5. 解析每页的Rectangle
            // 如果单页pdf中有多个Rectangle，则解析出来的List<Rectangle>会有多个对象
            while (pageIterator.hasNext()) {
                Page page = pageIterator.next();
                List<Rectangle> rectangleList = detectionAlgorithm.detect(page);
                if (rectangleList.size() > 0) {
                    pageNumber2RectangleListMap.put(page.getPageNumber(), rectangleList);
                }
            }

            // 6.通过table位置获取表格具体内容
            SpreadsheetExtractionAlgorithm extractionAlgorithm = new SpreadsheetExtractionAlgorithm();

            // 如果单页pdf中有多个表格，则解析出来的List<Rectangle>会有多个对象
            for (Map.Entry<Integer, List<Rectangle>> entry : pageNumber2RectangleListMap.entrySet()) {
                Page page = objectExtractor.extract(entry.getKey());
                Rectangle rectangle = null;

                List<Rectangle> rectangleList = entry.getValue();
                for (Rectangle r : rectangleList) {
                    double rectangleHeight = r.getHeight();
                    if (Double.compare(rectangleHeight, 0) > 0 && Double.compare(r.getWidth(), 0) > 0) {
                        rectangle = r;
                        break;
                    }
                }
                if (rectangle == null) {
                    throw new RuntimeException("rectangle is null.");
                }

                if (entry.getKey() > 1) {
                    rectangle.setTop(rectangle.getTop());
                }
                rectangle.setBottom(rectangle.getBottom() + 50);
                Page area = page.getArea(rectangle);

                // 如果每页有多个表格，解析每一个table
                List<Table> tableList = extractionAlgorithm.extract(area);
                for (Table table : tableList) {
                    for (int i = 0; i < table.getRowCount(); i++) {
                        List<String> cellList = new ArrayList<>();//一条记录，每个单元格占一个元素
                        for (int j = 0; j < table.getColCount(); j++) {
                            cellList.add(table.getCell(i, j).getText(false));
                        }
                        rowList.add(cellList);
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }

        return rowList;
    }


    @Override
    public String parsePdfHeaderText(String filePath) {
        String pdfHeaderText = "";
        try (PDDocument pdDocument = PDDocument.load(new File(filePath))) {
            PDPage page = pdDocument.getPages().get(0);
            removeWaterMark(page, pdDocument);

            PDFTextStripper stripper = new PDFTextStripper();
            stripper.setSortByPosition(true);
            stripper.setStartPage(1);
            stripper.setEndPage(1);
            pdfHeaderText = stripper.getText(pdDocument);
            pdfHeaderText = pdfHeaderText.replace(System.getProperty("line.separator", "\n"), "");
        } catch (Exception e) {
            log.error("OnError|{}||{}|{}|{}|{}", ErrorCode.FILE_PARSE_EXCEPTION.getCode(), "", "", "", "parsePdfHeaderText failed", e);
            throw new RuntimeException(e);
        }
        return pdfHeaderText;
    }

    private static void removeWaterMark(PDPage page, PDDocument pdDocument) throws IOException {
        Map<COSName, PDFont> oldfont = new HashMap<>();
        COSName fontName = null;
        PDFStreamParser pdfsp = new PDFStreamParser(page);
        pdfsp.parse();
        List<Object> tokens = pdfsp.getTokens();
        for (Object next : tokens) {
            if (next instanceof COSName) {
                fontName = (COSName) next;
                if (!oldfont.containsKey(fontName)) {
                    oldfont.put(fontName, page.getResources().getFont(fontName));
                }
            } else if (next instanceof COSString) {
                COSString previous = (COSString) next;
                try (InputStream in = new ByteArrayInputStream(previous.getBytes())) {
                    StringBuilder sb = new StringBuilder();
                    while (in.available() > 0 && oldfont.containsKey(fontName) && oldfont.get(fontName) != null) {
                        int rc = oldfont.get(fontName).readCode(in);
                        sb.append(oldfont.get(fontName).toUnicode(rc));
                    }
                    String text = sb.toString();
                    if (text.contains("PAB")) {
                        previous.setValue(oldfont.get(fontName).encode(""));
                    }
                }
            }
        }
        PDStream updatedStream = new PDStream(pdDocument);
        try (OutputStream out = updatedStream.createOutputStream()) {
            new ContentStreamWriter(out).writeTokens(tokens);
            page.setContents(updatedStream);
        }
    }

    public static void main(String[] args) {
        PABPdfParser2 pabPdfParser = new PABPdfParser2();
        PAB2 pab;
        String json;
//        pab = pabPdfParser.parsePABPdf("D:\\data\\file\\PAB\\zd4q0c2v1909108964610224128_c46774951df8d1f22aad9d15946d311d_beehive-pabc_jyls-0 (1).pdf");
//        json = JsonUtils.convertObjectToJson(pab);
//        System.out.println(json);

//        pab = pabPdfParser.parsePABPdf("D:\\data\\file\\PAB\\zd4asoqh1932011177263804416_8178f693cd976196686078f296511b7f_beehive-pabc_jyls-0.pdf");
//        json = JsonUtils.convertObjectToJson(pab);
//        System.out.println(json);


        pab = pabPdfParser.parsePABPdf("D:\\data\\file\\PAB\\zd4asoqh1932011177263804416_6ba370072faec8d6cdbec7845ff1c02f_beehive-pabc_jyls-0.pdf");
        json = JsonUtils.convertObjectToJson(pab);
        System.out.println(json);

    }

}
